/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.utils;

import java.beans.*;
import java.lang.reflect.*;
import java.util.*;
import org.junit.*;
import ch.msoftch.internal.*;
import ch.simpleel.internal.reflection.*;
import ch.simpleel.util.*;

/**
 * Utils class for Tests.
 * 
 * @author hautle
 */
public class BeanTestUtil {
    /**
     * Sorts the givne list by the id's.
     * 
     * @param <T> The bean type
     * 
     * @param list The list to sort
     * @param type The content type of the list
     * @param attr The name of the attribute used to sort the list
     * @return The sorted list
     */
    public static <T> List<T> sort(List<T> list, Class<T> type, String attr) {
        Collections.sort(list, new ElementValueComparator<T>(type, attr));
        return list;
    }

    /**
     * Checks the Beans in the given list for theyr equality.
     * 
     * @param message The message for the assertion
     * @param expected The list holding the expected values
     * @param actual The list to check
     * @param attrs The list of the properties to check (for each list entry)
     */
    public static void checkContentProperties(String message, List<?> expected, List<?> actual, String... attrs) throws IllegalArgumentException,
            TechnicalException, IllegalAccessException, InvocationTargetException {
        Assert.assertTrue(message + ": Not same element count", expected.size() == actual.size());
        for (int i = 0, cnt = expected.size(); i < cnt; i++)
            checkProperties(message, expected.get(i), actual.get(i), attrs);
    }

    /**
     * Checks the Beans in the given list for theyr equality.
     * 
     * @param message The message for the assertion
     * @param expected The list holding the expected values
     * @param actual The list to check
     * @param ignore The list of the properties to ignore
     * @throws Exception If something goes wrong
     */
    public static void checkBeans(String message, List<?> expected, List<?> actual, String... ignore) throws Exception {
        Assert.assertTrue(message + ": Not same element count", expected.size() == actual.size());
        for (int i = 0, cnt = expected.size(); i < cnt; i++)
            checkBeans(message, expected.get(i), actual.get(i), ignore);
    }

    /**
     * Checks two beans on their equalty.
     * 
     * @param <T> The bean type
     * 
     * @param message The message for the assertion
     * @param expected The bean holding the expected values
     * @param actual The bean to check
     * @param ignore The list of the properties to ignore
     */
    public static <T> void checkBeans(String message, T expected, T actual, String... ignore) throws Exception {
        Arrays.sort(ignore);
        for (PropertyDescriptor d : Introspector.getBeanInfo(expected.getClass()).getPropertyDescriptors()) {
            // skip ignore properties
            if (Arrays.binarySearch(ignore, d.getName()) > -1)
                continue;
            final Method read = d.getReadMethod();
            final Object valA = read.invoke(expected);
            final Object valB = read.invoke(actual);
            if (valA instanceof Collection || valA instanceof Object[]) {
                final List listA = asList(valA);
                final List listB = asList(valB);
                checkBeans(message, listA, listB);
                continue;
            }
            Assert.assertEquals(message + ": " + d.getName(), valA, valB);
        }
    }

    /**
     * Checks a serie of properties on theyr equality.
     * 
     * @param message The message for the assertion
     * @param expected The bean holding the expected values
     * @param actual The bean to check
     * @param attrs The list of the properties to check
     */
    public static void checkProperties(String message, Object expected, Object actual, String... attrs) throws IllegalArgumentException, TechnicalException,
            IllegalAccessException, InvocationTargetException {
        final Class<? extends Object> ca = expected.getClass();
        final Class<? extends Object> cb = actual.getClass();
        for (String attr : attrs) {
            final Object valA = ReflectionHelper.getDescriptor(ca, attr).getReadMethod().invoke(expected);
            final Object valB = ReflectionHelper.getDescriptor(cb, attr).getReadMethod().invoke(actual);
            Assert.assertEquals(message + ": " + attr, valA, valB);
        }
    }

    /**
     * Checks a serie of properties on theyr difference.
     * 
     * @param message The message for the assertion
     * @param expected The bean holding the expected values
     * @param actual The bean to check
     * @param attrs The list of the properties to check
     */
    public static void checkDifferentProperties(String message, Object expected, Object actual, String... attrs) throws IllegalArgumentException,
            TechnicalException, IllegalAccessException, InvocationTargetException {
        final Class<? extends Object> ca = expected.getClass();
        final Class<? extends Object> cb = actual.getClass();
        for (String attr : attrs) {
            final Object valA = ReflectionHelper.getDescriptor(ca, attr).getReadMethod().invoke(expected);
            final Object valB = ReflectionHelper.getDescriptor(cb, attr).getReadMethod().invoke(actual);
            Assert.assertTrue(message + ": " + attr, valA == null && valB != null || valB == null && valA != null || valA != null && !valA.equals(valB));
        }
    }

    /**
     * 'Converts' the given source to a collection.
     * 
     * @param src The source or null
     * @return The source as collection or null
     */
    public static List asList(Object src) {
        if (src == null)
            return null;
        if (src instanceof List)
            return (List) src;
        if (src instanceof Collection)
            return new ArrayList((Collection) src);
        return Arrays.asList((Object[]) src);
    }
}
